home *** CD-ROM | disk | FTP | other *** search
/ CICA 1995 August / CICA - The Ultimate Collection of Shareware for Windows (Disc 2) (August 1995).iso / disc2 / nt / source.exe / POSIX / ELVIS / VI.C < prev    next >
C/C++ Source or Header  |  1993-07-06  |  21KB  |  768 lines

  1. /* vi.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. #include "config.h"
  12. #include <ctype.h>
  13. #include "vi.h"
  14.  
  15.  
  16.  
  17. /* This array describes what each key does */
  18. #define NO_FUNC        (MARK (*)())0
  19. #define NO_ARGS        0
  20. #define CURSOR_COUNT    1
  21. #define CURSOR        2
  22. #define CURSOR_CNT_KEY    3
  23. #define CURSOR_MOVED    4
  24. #define CURSOR_EOL    5
  25. #define ZERO        6
  26. #define DIGIT        7
  27. #define CURSOR_TEXT    8
  28. #define CURSOR_CNT_CMD    9
  29. #define KEYWORD        10
  30. #define NO_FLAGS    0x00
  31. #define    MVMT        0x01    /* this is a movement command */
  32. #define PTMV        0x02    /* this can be *part* of a movement command */
  33. #define FRNT        0x04    /* after move, go to front of line */
  34. #define INCL        0x08    /* include last char when used with c/d/y */
  35. #define LNMD        0x10    /* use line mode of c/d/y */
  36. #define NCOL        0x20    /* this command can't change the column# */
  37. #define NREL        0x40    /* this is "non-relative" -- set the '' mark */
  38. #define SDOT        0x80    /* set the "dot" variables, for the "." cmd */
  39. static struct keystru
  40. {
  41.     MARK    (*func)();    /* the function to run */
  42.     uchar    args;        /* description of the args needed */
  43.     uchar    flags;        /* other stuff */
  44. }
  45.     vikeys[] =
  46. {
  47. /* NUL not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  48. /* ^A  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  49. /* ^B  page backward    */    {m_scroll,    CURSOR_CNT_CMD,    FRNT},
  50. /* ^C  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  51. /* ^D  scroll dn 1/2page*/    {m_scroll,    CURSOR_CNT_CMD,    NCOL},
  52. /* ^E  scroll up    */    {m_scroll,    CURSOR_CNT_CMD,    NCOL},
  53. /* ^F  page forward    */    {m_scroll,    CURSOR_CNT_CMD,    FRNT},
  54. /* ^G  show file status    */    {v_status,    NO_ARGS,     NO_FLAGS},
  55. /* ^H  move left, like h*/    {m_left,    CURSOR_COUNT,    MVMT},
  56. /* ^I  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  57. /* ^J  move down    */    {m_updnto,    CURSOR_CNT_CMD,    MVMT|LNMD},
  58. /* ^K  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  59. /* ^L  redraw screen    */    {v_redraw,    NO_ARGS,    NO_FLAGS},
  60. /* ^M  mv front next ln */    {m_updnto,    CURSOR_CNT_CMD,    MVMT|FRNT|LNMD},
  61. /* ^N  move down    */    {m_updnto,    CURSOR_CNT_CMD,    MVMT|LNMD},
  62. /* ^O  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  63. /* ^P  move up        */    {m_updnto,    CURSOR_CNT_CMD,    MVMT|LNMD},
  64. /* ^Q  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  65. /* ^R  redraw screen    */    {v_redraw,    NO_ARGS,    NO_FLAGS},
  66. /* ^S  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  67. /* ^T  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  68. /* ^U  scroll up 1/2page*/    {m_scroll,    CURSOR_CNT_CMD,    NCOL},
  69. /* ^V  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  70. /* ^W  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  71. /* ^X  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  72. /* ^Y  scroll down    */    {m_scroll,    CURSOR_CNT_CMD,    NCOL},
  73. /* ^Z  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  74. /* ESC not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  75. /* ^\  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  76. /* ^]  keyword is tag    */    {v_tag,        KEYWORD,    NO_FLAGS},
  77. /* ^^  previous file    */    {v_switch,    CURSOR,        NO_FLAGS},
  78. /* ^_  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  79. /* SPC move right,like l*/    {m_right,    CURSOR_COUNT,    MVMT},
  80. /*  !  run thru filter    */    {v_filter,    CURSOR_MOVED,    FRNT|LNMD|INCL},
  81. /*  "  select cut buffer*/    {v_selcut,    CURSOR_CNT_KEY,    PTMV},
  82. #ifndef NO_EXTENSIONS
  83. /*  #  increment number    */    {v_increment,    KEYWORD,    SDOT},
  84. #else
  85. /*  #  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  86. #endif
  87. /*  $  move to rear    */    {m_rear,    CURSOR,        MVMT|INCL},
  88. /*  %  move to match    */    {m_match,    CURSOR,        MVMT|INCL},
  89. /*  &  repeat subst    */    {v_again,    CURSOR_MOVED,    SDOT|NCOL|LNMD|INCL},
  90. /*  '  move to a mark    */    {m_tomark,    CURSOR_CNT_KEY,    MVMT|FRNT|NREL|LNMD|INCL},
  91. #ifndef NO_SENTENCE
  92. /*  (  mv back sentence    */    {m_bsentence,    CURSOR_COUNT,    MVMT},
  93. /*  )  mv fwd sentence    */    {m_fsentence,    CURSOR_COUNT,    MVMT},
  94. #else
  95. /*  (  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  96. /*  )  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  97. #endif
  98. #ifndef NO_ERRLIST
  99. /*  *  errlist        */    {v_errlist,    CURSOR,        FRNT|NREL},
  100. #else
  101. /*  *  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  102. #endif
  103. /*  +  mv front next ln */    {m_updnto,    CURSOR_CNT_CMD,    MVMT|FRNT|LNMD},
  104. #ifndef NO_CHARSEARCH
  105. /*  ,  reverse [fFtT] cmd*/    {m__ch,        CURSOR_CNT_CMD,    MVMT|INCL},
  106. #else
  107. /*  ,  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  108. #endif
  109. /*  -  mv front prev ln    */    {m_updnto,    CURSOR_CNT_CMD,    MVMT|FRNT|LNMD},
  110. /*  .  special...    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  111. /*  /  forward search    */    {m_fsrch,    CURSOR_TEXT,    MVMT|NREL},
  112. /*  0  part of count?    */    {NO_FUNC,    ZERO,        MVMT|PTMV},
  113. /*  1  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  114. /*  2  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  115. /*  3  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  116. /*  4  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  117. /*  5  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  118. /*  6  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  119. /*  7  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  120. /*  8  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  121. /*  9  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  122. /*  :  run single EX cmd*/    {v_1ex,        CURSOR_TEXT,    NO_FLAGS},
  123. #ifndef NO_CHARSEARCH
  124. /*  ;  repeat [fFtT] cmd*/    {m__ch,        CURSOR_CNT_CMD,    MVMT|INCL},
  125. #else
  126. /*  ;  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  127. #endif
  128. /*  <  shift text left    */    {v_lshift,    CURSOR_MOVED,    SDOT|FRNT|LNMD|INCL},
  129. /*  =  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  130. /*  >  shift text right    */    {v_rshift,    CURSOR_MOVED,    SDOT|FRNT|LNMD|INCL},
  131. /*  ?  backward search    */    {m_bsrch,    CURSOR_TEXT,    MVMT|NREL},
  132. #ifndef NO_AT
  133. /*  @  execute a cutbuf */    {v_at,        CURSOR_CNT_KEY,    NO_FLAGS},
  134. #else
  135. /*  @  undefined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  136. #endif
  137. /*  A  append at EOL    */    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  138. /*  B  move back Word    */    {m_bword,    CURSOR_CNT_CMD,    MVMT},
  139. /*  C  change to EOL    */    {v_change,    CURSOR_EOL,    SDOT},
  140. /*  D  delete to EOL    */    {v_delete,    CURSOR_EOL,    SDOT},
  141. /*  E  move end of Word    */    {m_eword,    CURSOR_CNT_CMD,    MVMT|INCL},
  142. #ifndef NO_CHARSEARCH
  143. /*  F  move bk to char    */    {m_Fch,        CURSOR_CNT_KEY,    MVMT|INCL},
  144. #else
  145. /*  F  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  146. #endif
  147. /*  G  move to line #    */    {m_updnto,    CURSOR_CNT_CMD,    MVMT|NREL|LNMD|FRNT|INCL},
  148. /*  H  move to row    */    {m_row,        CURSOR_CNT_CMD,    MVMT|FRNT},
  149. /*  I  insert at front    */    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  150. /*  J  join lines    */    {v_join,    CURSOR_COUNT,    SDOT},
  151. #ifndef NO_EXTENSIONS
  152. /*  K  look up keyword    */    {v_keyword,    KEYWORD,    NO_FLAGS},
  153. #else
  154. /*  K  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  155. #endif
  156. /*  L  move to last row    */    {m_row,        CURSOR_CNT_CMD,    MVMT|FRNT},
  157. /*  M  move to mid row    */    {m_row,        CURSOR_CNT_CMD,    MVMT|FRNT},
  158. /*  N  reverse prev srch*/    {m_Nsrch,    CURSOR,        MVMT},
  159. /*  O  insert above line*/    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  160. /*  P  paste before    */    {v_paste,    CURSOR_CNT_CMD,    NO_FLAGS},
  161. /*  Q  quit to EX mode    */    {v_quit,    NO_ARGS,    NO_FLAGS},
  162. /*  R  overtype        */    {v_overtype,    CURSOR,        SDOT},
  163. /*  S  change line    */    {v_change,    CURSOR_MOVED,    SDOT},
  164. #ifndef NO_CHARSEARCH
  165. /*  T  move bk to char    */    {m_Tch,        CURSOR_CNT_KEY,    MVMT|INCL},
  166. #else
  167. /*  T  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  168. #endif
  169. /*  U  undo whole line    */    {v_undoline,    CURSOR,        FRNT},
  170. /*  V  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  171. /*  W  move forward Word*/    {m_fword,    CURSOR_CNT_CMD,    MVMT},
  172. /*  X  delete to left    */    {v_xchar,    CURSOR_CNT_CMD,    SDOT},
  173. /*  Y  yank text    */    {v_yank,    CURSOR_MOVED,    NCOL},
  174. /*  Z  save file & exit    */    {v_xit,        CURSOR_CNT_KEY,    NO_FLAGS},
  175. /*  [  move back section*/    {m_bsection,    CURSOR_CNT_KEY,    MVMT|LNMD|NREL},
  176. /*  \  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  177. /*  ]  move fwd section */    {m_fsection,    CURSOR_CNT_KEY,    MVMT|LNMD|NREL},
  178. /*  ^  move to front    */    {m_front,    CURSOR,        MVMT},
  179. /*  _  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  180. /*  `  move to mark    */    {m_tomark,    CURSOR_CNT_KEY,    MVMT|NREL},
  181. /*  a  append at cursor    */    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  182. /*  b  move back word    */    {m_bword,    CURSOR_CNT_CMD,    MVMT},
  183. /*  c  change text    */    {v_change,    CURSOR_MOVED,    SDOT},
  184. /*  d  delete op    */    {v_delete,    CURSOR_MOVED,    SDOT|NCOL},
  185. /*  e  move end word    */    {m_eword,    CURSOR_CNT_CMD,    MVMT|INCL},
  186. #ifndef NO_CHARSEARCH
  187. /*  f  move fwd for char*/    {m_fch,        CURSOR_CNT_KEY,    MVMT|INCL},
  188. #else
  189. /*  f  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  190. #endif
  191. /*  g  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  192. /*  h  move left    */    {m_left,    CURSOR_COUNT,    MVMT},
  193. /*  i  insert at cursor    */    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  194. /*  j  move down    */    {m_updnto,    CURSOR_CNT_CMD,    MVMT|NCOL|LNMD},
  195. /*  k  move up        */    {m_updnto,    CURSOR_CNT_CMD,    MVMT|NCOL|LNMD},
  196. /*  l  move right    */    {m_right,    CURSOR_COUNT,    MVMT},
  197. /*  m  define a mark    */    {v_mark,    CURSOR_CNT_KEY,    NO_FLAGS},
  198. /*  n  repeat prev srch    */    {m_nsrch,    CURSOR,     MVMT},
  199. /*  o  insert below line*/    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  200. /*  p  paste after    */    {v_paste,    CURSOR_CNT_CMD,    NO_FLAGS},
  201. /*  q  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  202. /*  r  replace chars    */    {v_replace,    CURSOR_CNT_KEY,    SDOT},
  203. /*  s  subst N chars    */    {v_subst,    CURSOR_COUNT,    SDOT},
  204. #ifndef NO_CHARSEARCH
  205. /*  t  move fwd to char    */    {m_tch,        CURSOR_CNT_KEY,    MVMT|INCL},
  206. #else
  207. /*  t  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  208. #endif
  209. /*  u  undo        */    {v_undo,    CURSOR,        NO_FLAGS},
  210. /*  v  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  211. /*  w  move fwd word    */    {m_fword,    CURSOR_CNT_CMD,    MVMT},
  212. /*  x  delete character    */    {v_xchar,    CURSOR_CNT_CMD,    SDOT},
  213. /*  y  yank text    */    {v_yank,    CURSOR_MOVED,    NCOL},
  214. /*  z  adjust scrn row    */    {m_z,         CURSOR_CNT_KEY,    NCOL},
  215. /*  {  back paragraph    */    {m_bparagraph,    CURSOR_COUNT,    MVMT|LNMD},
  216. /*  |  move to column    */    {m_tocol,    CURSOR_COUNT,    NREL},
  217. /*  }  fwd paragraph    */    {m_fparagraph,    CURSOR_COUNT,    MVMT|LNMD},
  218. /*  ~  upper/lowercase    */    {v_ulcase,    CURSOR_COUNT,    SDOT},
  219. /* DEL not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS}
  220. };
  221.  
  222.  
  223.  
  224. void vi()
  225. {
  226.     REG int            key;    /* keystroke from user */
  227.     long            count;    /* numeric argument to some functions */
  228.     REG struct keystru    *keyptr;/* pointer to vikeys[] element */
  229.     MARK            tcurs;    /* temporary cursor */
  230.     int            prevkey;/* previous key, if d/c/y/</>/! */
  231.     MARK            range;    /* start of range for d/c/y/</>/! */
  232.     char            text[100];
  233.     int            dotkey;    /* last "key" of a change */
  234.     int            dotpkey;/* last "prevkey" of a change */
  235.     int            dotkey2;/* last extra "getkey()" of a change */
  236.     int            dotcnt;    /* last "count" of a change */
  237.     int            firstkey;
  238.     REG int            i;
  239.  
  240.     /* tell the redraw() function to start from scratch */
  241.     redraw(MARK_UNSET, FALSE);
  242.  
  243. #ifdef lint
  244.     /* lint says that "range" might be used before it is set.  This
  245.      * can't really happen due to the way "range" and "prevkey" are used,
  246.      * but lint doesn't know that.  This line is here ONLY to keep lint
  247.      * happy.
  248.      */
  249.     range = 0L;
  250. #endif
  251.  
  252.     /* safeguard against '.' with no previous command */
  253.     dotkey = 0;
  254.  
  255.     /* go immediately into insert mode, if ":set inputmode" */
  256.     firstkey = 0;
  257. #ifndef NO_EXTENSIONS
  258.     if (*o_inputmode)
  259.     {
  260.         firstkey = 'i';
  261.     }
  262. #endif
  263.  
  264.     /* Repeatedly handle VI commands */
  265.     for (count = 0, prevkey = '\0'; mode == MODE_VI; )
  266.     {
  267.         /* if we've moved off the undoable line, then we can't undo it at all */
  268.         if (markline(cursor) != U_line)
  269.         {
  270.             U_line = 0L;
  271.         }
  272.  
  273.         /* report any changes from the previous command */
  274.         if (rptlines >= *o_report)
  275.         {
  276.             redraw(cursor, FALSE);
  277.             msg("%ld lines %s", rptlines, rptlabel);
  278.         }
  279.         rptlines = 0L;
  280.  
  281.         /* get the next command key.  It must be ASCII */
  282.         if (firstkey)
  283.         {
  284.             key = firstkey;
  285.             firstkey = 0;
  286.         }
  287.         else
  288.         {
  289.             do
  290.             {
  291.                 key = getkey(WHEN_VICMD);
  292.             } while (key < 0 || key > 127);
  293.         }
  294.  
  295.         /* change cw and cW commands to ce and cE, respectively */
  296.         /* (Why?  because the real vi does it that way!) */
  297.         if (prevkey == 'c')
  298.         {
  299.             if (key == 'w')
  300.                 key = 'e';
  301.             else if (key == 'W')
  302.                 key = 'E';
  303.  
  304.             /* wouldn't work right at the end of a word unless we
  305.              * backspace one character before doing the move.  This
  306.              * will fix most cases.  !!! but not all.
  307.              */
  308.             if (markidx(cursor) > 0 && (key == 'e' || key == 'E'))
  309.             {
  310.                 cursor--;
  311.             }
  312.         }
  313.  
  314.         /* look up the structure describing this command */
  315.         keyptr = &vikeys[key];
  316.  
  317.         /* if we're in the middle of a d/c/y/</>/! command, reject
  318.          * anything but movement or a doubled version like "dd".
  319.          */
  320.         if (prevkey && key != prevkey && !(keyptr->flags & (MVMT|PTMV)))
  321.         {
  322.             beep();
  323.             prevkey = 0;
  324.             count = 0;
  325.             continue;
  326.         }
  327.  
  328.         /* set the "dot" variables, if we're supposed to */
  329.         if ((keyptr->flags & SDOT)
  330.          || (prevkey && vikeys[prevkey].flags & SDOT))
  331.         {
  332.             dotkey = key;
  333.             dotpkey = prevkey;
  334.             dotkey2 = '\0';
  335.             dotcnt = count;
  336.  
  337.             /* remember the line before any changes are made */
  338.             if (U_line != markline(cursor))
  339.             {
  340.                 U_line = markline(cursor);
  341.                 strcpy(U_text, fetchline(U_line));
  342.             }
  343.         }
  344.  
  345.         /* if this is "." then set other vars from the "dot" vars */
  346.         if (key == '.')
  347.         {
  348.             key = dotkey;
  349.             keyptr = &vikeys[key];
  350.             prevkey = dotpkey;
  351.             if (prevkey)
  352.             {
  353.                 range = cursor;
  354.             }
  355.             if (count == 0)
  356.             {
  357.                 count = dotcnt;
  358.             }
  359.             doingdot = TRUE;
  360.  
  361.             /* remember the line before any changes are made */
  362.             if (U_line != markline(cursor))
  363.             {
  364.                 U_line = markline(cursor);
  365.                 strcpy(U_text, fetchline(U_line));
  366.             }
  367.         }
  368.         else
  369.         {
  370.             doingdot = FALSE;
  371.         }
  372.  
  373.         /* process the key as a command */
  374.         tcurs = cursor;
  375.         switch (keyptr->args)
  376.         {
  377.           case ZERO:
  378.             if (count == 0)
  379.             {
  380.                 tcurs = cursor & ~(BLKSIZE - 1);
  381.                 break;
  382.             }
  383.             /* else fall through & treat like other digits... */
  384.  
  385.           case DIGIT:
  386.             count = count * 10 + key - '0';
  387.             break;
  388.  
  389.           case KEYWORD:
  390.             /* if not on a keyword, fail */
  391.             pfetch(markline(cursor));
  392.             key = markidx(cursor);
  393.             if (isascii(ptext[key])
  394.                 && !isalnum(ptext[key]) && ptext[key] != '_')
  395.             {
  396.                 tcurs = MARK_UNSET;
  397.                 break;
  398.             }
  399.  
  400.             /* find the start of the keyword */
  401.             while (key > 0 && (!isascii(ptext[key-1]) ||
  402.             isalnum(ptext[key - 1]) || ptext[key - 1] == '_'))
  403.             {
  404.                 key--;
  405.             }
  406.             tcurs = (cursor & ~(BLKSIZE - 1)) + key;
  407.  
  408.             /* copy it into a buffer, and NUL-terminate it */
  409.             i = 0;
  410.             do
  411.             {
  412.                 text[i++] = ptext[key++];
  413.             } while (!isascii(ptext[key]) || isalnum(ptext[key]) || ptext[key] == '_');
  414.             text[i] = '\0';
  415.  
  416.             /* call the function */
  417.             tcurs = (*keyptr->func)(text, tcurs, count);
  418.             count = 0L;
  419.             break;
  420.  
  421.           case NO_ARGS:
  422.             if (keyptr->func)
  423.             {
  424.                 (*keyptr->func)();
  425.             }
  426.             else
  427.             {
  428.                 beep();
  429.             }
  430.             count = 0L;
  431.             break;
  432.     
  433.           case CURSOR_COUNT:
  434.             tcurs = (*keyptr->func)(cursor, count);
  435.             count = 0L;
  436.             break;
  437.     
  438.           case CURSOR:
  439.             tcurs = (*keyptr->func)(cursor);
  440.             count = 0L;
  441.             break;
  442.  
  443.           case CURSOR_CNT_KEY:
  444.             if (doingdot)
  445.             {
  446.                 tcurs = (*keyptr->func)(cursor, count, dotkey2);
  447.             }
  448.             else
  449.             {
  450.                 /* get a key */
  451.                 i = getkey(0);
  452.                 if (i == '\033') /* ESC */
  453.                 {
  454.                     count = 0;
  455.                     tcurs = MARK_UNSET;
  456.                     break; /* exit from "case CURSOR_CNT_KEY" */
  457.                 }
  458.                 else if (i == ('V' & 0x1f))
  459.                 {
  460.                     i = getkey(0);
  461.                 }
  462.  
  463.                 /* if part of an SDOT command, remember it */
  464.                  if (keyptr->flags & SDOT
  465.                  || (prevkey && vikeys[prevkey].flags & SDOT))
  466.                 {
  467.                     dotkey2 = i;
  468.                 }
  469.  
  470.                 /* do it */
  471.                 tcurs = (*keyptr->func)(cursor, count, i);
  472.             }
  473.             count = 0L;
  474.             break;
  475.     
  476.           case CURSOR_MOVED:
  477.             /* '&' and uppercase keys always act like doubled */
  478.             if (key == '&' || isascii(key) && isupper(key))
  479.             {
  480.                 prevkey = key;
  481.             }
  482.  
  483.             if (prevkey)
  484.             {
  485.                 /* doubling up a command */
  486.                 if (!count) count = 1L;
  487.                 range = cursor;
  488.                 tcurs = range + MARK_AT_LINE(count - 1L);
  489.                 count = 0L;
  490.             }
  491.             else
  492.             {
  493.                 prevkey = key;
  494.                 range = cursor;
  495.                 key = -1; /* so we don't think we doubled yet */
  496.             }
  497.             break;
  498.  
  499.           case CURSOR_EOL:
  500.             prevkey = key;
  501.             /* a zero-length line needs special treatment */
  502.             pfetch(markline(cursor));
  503.             if (plen == 0)
  504.             {
  505.                 /* act on a zero-length section of text */
  506.                 range = tcurs = cursor;
  507.                 key = ' ';
  508.             }
  509.             else
  510.             {
  511.                 /* act like CURSOR_MOVED with '$' movement */
  512.                 range = cursor;
  513.                 tcurs = m_rear(cursor, 1L);
  514.                 key = '$';
  515.             }
  516.             count = 0L;
  517.             keyptr = &vikeys[key];
  518.             break;
  519.  
  520.           case CURSOR_TEXT:
  521.               do
  522.               {    
  523.                 text[0] = key;
  524.                 if (vgets(key, text + 1, sizeof text - 1) >= 0)
  525.                 {
  526.                     /* reassure user that <CR> was hit */
  527.                     qaddch('\r');
  528.                     refresh();
  529.  
  530.                     /* call the function with the text */
  531.                     tcurs = (*keyptr->func)(cursor, text);
  532.                 }
  533.                 else
  534.                 {
  535.                     if (exwrote || mode == MODE_COLON)
  536.                     {
  537.                         redraw(MARK_UNSET, FALSE);
  538.                     }
  539.                     mode = MODE_VI;
  540.                 }
  541.             } while (mode == MODE_COLON);
  542.             count = 0L;
  543.             break;
  544.  
  545.           case CURSOR_CNT_CMD:
  546.             tcurs = (*keyptr->func)(cursor, count, key);
  547.             count = 0L;
  548.             break;
  549.         }
  550.  
  551.         /* if that command took us out of vi mode, then exit the loop
  552.          * NOW, without tweaking the cursor or anything.  This is very
  553.          * important when mode == MODE_QUIT.
  554.          */
  555.         if (mode != MODE_VI)
  556.         {
  557.             break;
  558.         }
  559.  
  560.         /* now move the cursor, as appropriate */
  561.         if (keyptr->args == CURSOR_MOVED)
  562.         {
  563.             /* the < and > keys have FRNT,
  564.              * but it shouldn't be applied yet
  565.              */
  566.             tcurs = adjmove(cursor, tcurs, 0);
  567.         }
  568.         else
  569.         {
  570.             tcurs = adjmove(cursor, tcurs, (int)keyptr->flags);
  571.         }
  572.  
  573.         /* was that the end of a d/c/y/</>/! command? */
  574.         if (prevkey && (prevkey == key || (keyptr->flags & MVMT)) && count == 0L)
  575.         {
  576.             /* if the movement command failed, cancel operation */
  577.             if (tcurs == MARK_UNSET)
  578.             {
  579.                 prevkey = 0;
  580.                 count = 0;
  581.                 continue;
  582.             }
  583.  
  584.             /* make sure range=front and tcurs=rear.  Either way,
  585.              * leave cursor=range since that's where we started.
  586.              */
  587.             cursor = range;
  588.             if (tcurs < range)
  589.             {
  590.                 range = tcurs;
  591.                 tcurs = cursor;
  592.             }
  593.  
  594.  
  595.             /* adjust for line mode & inclusion of last char/line */
  596.             i = (keyptr->flags | vikeys[prevkey].flags);
  597.             if (key == prevkey)
  598.             {
  599.                 i |= (INCL|LNMD);
  600.             }
  601.             switch (i & (INCL|LNMD))
  602.             {
  603.               case INCL:
  604.                 tcurs++;
  605.                 break;
  606.  
  607.               case INCL|LNMD:
  608.                 tcurs += BLKSIZE;
  609.                 /* fall through... */
  610.  
  611.               case LNMD:
  612.                 range &= ~(BLKSIZE - 1);
  613.                 tcurs &= ~(BLKSIZE - 1);
  614.                 break;
  615.             }
  616.  
  617.             /* run the function */
  618.             tcurs = (*vikeys[prevkey].func)(range, tcurs);
  619.             (void)adjmove(cursor, cursor, 0);
  620.             cursor = adjmove(cursor, tcurs, (int)vikeys[prevkey].flags);
  621.  
  622.             /* cleanup */
  623.             prevkey = 0;
  624.         }
  625.         else if (!prevkey)
  626.         {
  627.             cursor = tcurs;
  628.         }
  629.     }
  630. }
  631.  
  632. /* This function adjusts the MARK value that they return; here we make sure
  633.  * it isn't past the end of the line, and that the column hasn't been
  634.  * *accidentally* changed.
  635.  */
  636. MARK adjmove(old, new, flags)
  637.     MARK        old;    /* the cursor position before the command */
  638.     REG MARK    new;    /* the cursor position after the command */
  639.     int        flags;    /* various flags regarding cursor mvmt */
  640. {
  641.     static int    colno;    /* the column number that we want */
  642.     REG char    *text;    /* used to scan through the line's text */
  643.     REG int        i;
  644.  
  645. #ifdef DEBUG
  646.     watch();
  647. #endif
  648.  
  649.     /* if the command failed, bag it! */
  650.     if (new == MARK_UNSET)
  651.     {
  652.         beep();
  653.         return old;
  654.     }
  655.  
  656.     /* if this is a non-relative movement, set the '' mark */
  657.     if (flags & NREL)
  658.     {
  659.         mark[26] = old;
  660.     }
  661.  
  662.     /* make sure it isn't past the end of the file */
  663.     if (markline(new) < 1)
  664.     {
  665.         new = MARK_FIRST;
  666.     }
  667.     else if (markline(new) > nlines)
  668.     {
  669.         new = MARK_LAST;
  670.     }
  671.  
  672.     /* fetch the new line */
  673.     pfetch(markline(new));
  674.  
  675.     /* move to the front, if we're supposed to */
  676.     if (flags & FRNT)
  677.     {
  678.         new = m_front(new, 1L);
  679.     }
  680.  
  681.     /* change the column#, or change the mark to suit the column# */
  682.     if (!(flags & NCOL))
  683.     {
  684.         /* change the column# */
  685.         i = markidx(new);
  686.         if (i == BLKSIZE - 1)
  687.         {
  688.             new &= ~(BLKSIZE - 1);
  689.             if (plen > 0)
  690.             {
  691.                 new += plen - 1;
  692.             }
  693.             colno = BLKSIZE * 8; /* one heck of a big colno */
  694.         }
  695.         else if (plen > 0)
  696.         {
  697.             if (i >= plen)
  698.             {
  699.                 new = (new & ~(BLKSIZE - 1)) + plen - 1;
  700.             }
  701.             colno = idx2col(new, ptext, FALSE);
  702.         }
  703.         else
  704.         {
  705.             new &= ~(BLKSIZE - 1);
  706.             colno = 0;
  707.         }
  708.     }
  709.     else
  710.     {
  711.         /* adjust the mark to get as close as possible to column# */
  712.         for (i = 0, text = ptext; i <= colno && *text; text++)
  713.         {
  714.             if (*text == '\t' && !*o_list)
  715.             {
  716.                 i += *o_tabstop - (i % *o_tabstop);
  717.             }
  718.             else if (UCHAR(*text) < ' ' || *text == 127)
  719.             {
  720.                 i += 2;
  721.             }
  722. #ifndef NO_CHARATTR
  723.             else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2])
  724.             {
  725.                 text += 2; /* plus one more in "for()" stmt */
  726.             }
  727. #endif
  728.             else
  729.             {
  730.                 i++;
  731.             }
  732.         }
  733.         if (text > ptext)
  734.         {
  735.             text--;
  736.         }
  737.         new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext);
  738.     }
  739.  
  740.     return new;
  741. }
  742.  
  743.  
  744. #ifdef DEBUG
  745. watch()
  746. {
  747.     static wasset;
  748.  
  749.     if (*origname)
  750.     {
  751.         wasset = TRUE;
  752.     }
  753.     else if (wasset)
  754.     {
  755.         msg("origname was clobbered");
  756.         endwin();
  757.         abort();
  758.     }
  759.  
  760.     if (nlines == 0)
  761.     {
  762.         msg("nlines=0");
  763.         endwin();
  764.         abort();
  765.     }
  766. }
  767. #endif
  768.